import { ApiCode } from '@/constants/api';
import { ElNotification } from 'element-plus';
import { reactive } from 'vue';
import { SimulateController } from './Controller';
import local from '@/util/local';

export enum BreakpointTriggerType {
  onEntry = 1,
  onExit = 2,
  onBoth = 3
}

export type BreakpointItem = {
  enabled: boolean,
  modelId: string,
  displayName: string,
  displayIcon: string,
  condition: string,
  trigger: BreakpointTriggerType
};

export default class BreakPointsService {
  controller: SimulateController;
  breakpointList: BreakpointItem[];
  /** 选中的行 */
  selectedRows: BreakpointItem[] = [];
  DEFAULT_ENABLED = true;
  DEFAULT_CONDITION = "";
  DEFAULT_TRIGGER : BreakpointTriggerType = BreakpointTriggerType.onEntry;

  constructor(controller: SimulateController) {
    this.controller = controller;
    this.breakpointList = reactive([]);
  }

  // get all breakpoints from node-side
  async refreshFromModel() {
    if (!this.controller.projectId) return;
    try {
      const res = await this.controller.getBreakpoints();
      const resData = res.data;
      if (resData.code !== ApiCode.SUCCESS) throw resData;
      const rawBreakpointsData = resData.data as {
        modelId: string,
        condition: string,
        enabled: boolean,
        suspend: BreakpointTriggerType,
      }[];

      if (rawBreakpointsData.length) {
        const mountedModelIds = rawBreakpointsData.map(item => item.modelId);
        const mountedModelInfosRes = await this.controller.queryModelInfos(mountedModelIds);
        const mountedModelInfosResData = mountedModelInfosRes.data;
        if (mountedModelInfosResData.code !== ApiCode.SUCCESS) throw mountedModelInfosResData;
        const mountedModelInfos = mountedModelInfosResData.data as {
          icon: string,
          name: string,
          id: string,
          showAttribute?: {
            sourceName?: string,
            targetName?: string
          }
        }[];
        let modelId2BreakpointMap = new Map<string, {
          modelId: string,
          condition: string,
          enabled: boolean,
          suspend: BreakpointTriggerType
        }>();
        rawBreakpointsData.forEach(item => {
          modelId2BreakpointMap.set(item.modelId, item);
        });
        const newBreakpointList = mountedModelInfos.map(item => {
          const rawBreakpoint = modelId2BreakpointMap.get(item.id)!;
          return {
            displayName: item.name ? item.name : `${item.showAttribute?.sourceName} -> ${item.showAttribute?.targetName}`,
            displayIcon: item.icon,
            modelId: rawBreakpoint.modelId,
            condition: rawBreakpoint.condition,
            enabled: rawBreakpoint.enabled ? true : false,
            trigger: rawBreakpoint.suspend,
          };
        });
        this.breakpointList.splice(0);
        Object.assign(this.breakpointList, newBreakpointList);
      } else {
        this.breakpointList.splice(0);
        Object.assign(this.breakpointList, []);
      }
    } catch (err: any) {
      ElNotification({
        title: local.localeTrans('fail_get_info'),
        message: err.message ? err.message : local.localeTrans('check_log'),
        type: 'error',
      });
      console.error(err);
    }
  }

  async addBreakpoint(modelId: string) {
    try {
      const mountedModelInfoRes = await this.controller.queryModelInfo(modelId);
      const mountedModelInfoResData = mountedModelInfoRes.data;
      if (mountedModelInfoResData.code !== ApiCode.SUCCESS) throw mountedModelInfoResData;
      const mountedModelInfo = mountedModelInfoResData.data;

      const res = await this.controller.addBreakpoints(modelId);
      const resData = res.data;
      if (resData.code !== ApiCode.SUCCESS) throw resData;

      this.breakpointList.push({
        enabled: this.DEFAULT_ENABLED,
        modelId: modelId,
        displayName: mountedModelInfo.name,
        displayIcon: mountedModelInfo.icon,
        condition: this.DEFAULT_CONDITION,
        trigger: this.DEFAULT_TRIGGER
      });
      this.notifyEdition();
    } catch (err: any) {
      ElNotification({
        title: local.localeTrans('f_breakpoint'),
        message: err.message ? err.message : local.localeTrans('check_log'),
        type: 'error',
      });
      console.error(err);
    }
  }

  async removeBreakpoints() {
    try {
      const modelIds = this.selectedRows ? this.selectedRows.map(it => it.modelId) : [];
      if (modelIds.length === 0) return;
      const removeRes = await this.controller.deleteBreakpoints(modelIds);
      const removeResData = removeRes.data;
      if (removeResData.code !== ApiCode.SUCCESS) throw removeResData;

      let modelIdSet = new Set<string>();
      modelIdSet.addAll(modelIds);
      this.breakpointList = this.breakpointList.filter(item => !modelIdSet.has(item.modelId));
      this.notifyEdition();
    } catch (err: any) {
      ElNotification({
        title: this.controller.locale.localeTrans('f_remove_point'),
        message: err.message ? err.message : this.controller.locale.localeTrans('check_log'),
        type: 'error',
      });
      console.error(err);
    }
  }

  async removeAllBreakpoints() {
    try {
      const deleteAllRes = await this.controller.deleteAllBreakpoints();
      if (deleteAllRes.data.code !== ApiCode.SUCCESS) throw deleteAllRes.data;
      this.breakpointList.splice(0);
      await this.notifyEdition();
    } catch (err: any) {
      ElNotification({
        title: this.controller.locale.localeTrans('f_clear_point'),
        message: err.message ? err.message : this.controller.locale.localeTrans('check_log'),
        type: 'error',
      });
      console.error(err);
    }
  }

  // true->false, false->true 重构
  async setEnabledAt(index: number, enabled: boolean) {
    this.commitEdit(index, "enabled", enabled);
  }

  async setTriggerAt(index: number, trigger: BreakpointTriggerType) {
    this.commitEdit(index, "suspend", trigger);
  }

  async setConditionAt(index: number, condition: string) {
    this.commitEdit(index, "condition", condition);
  }

  async commitEdit(index: number, editField: "condition" | "enabled" | "suspend", value: string | boolean | BreakpointTriggerType) {
    try {
      const editRes = await this.controller.editBreakpoint(this.breakpointList[index].modelId, editField, value);
      if (editRes.data.code !== ApiCode.SUCCESS) throw editRes.data;
      await this.notifyEdition();
    } catch (err: any) {
      ElNotification({
        title: local.localeTrans('f_m_point'),
        message: err.message ? err.message : local.localeTrans('check_log'),
        type: 'error',
      });
      console.error(err);
    }
  }

  async notifyEdition() {
    await this.controller.notifySimBackendBreakpoints();
  }
}
